package com.bjsxt.controller;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.bjsxt.costant.Constants;
import com.bjsxt.model.Result;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * description:
 * 文件上传控制层
 * 文件上传的权限:
 * TODO: 这里的访问权限控制在书写的时候对应的权限表没有对应的权限, 需要后期加入, 权限访问还是要加的, 暂时注释掉
 *
 * @author ALIENWARE <br />
 * @date 2021/7/18
 */
@RestController
@RequestMapping("/image")
@Api(tags = "文件上传控制器")
public class FileController {

    /**
     * OSS 文件上传操作对象客户端
     */
    @Autowired
    private OSS ossClient;

    /**
     * Alibaba-OOS 对象上传服务的 AccessKey
     */
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessKey;

    /**
     * bucketName 的声明
     */
    @Value("${oss.bucket.name:coin-exchange-oldwang}")
    private String bucketName;

    /**
     * oss 对象存储控制台地域节点(Endpoint)
     */
    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endPoint;

    /**
     * OOS 服务上传的回调地址
     */
    @Value("${spring.cloud.alicloud.oss.oos-callback-url: https://aba6-123-172-53-91.ngrok-free.app}")
    private String oosCallbackUrl;

    /**
     * 利用阿里云 OOS 对象存储服务队文件进行上传操作
     *
     * @param file 需要进行上传的文件
     * @return 统一响应对象
     * @throws IOException 文件读取异常
     */
    @PostMapping(value = "/AliYunImgUpload", consumes = "multipart/*", headers = "content-type=multipart/form-date")
    // @PreAuthorize("hasAuthority('sys_role_query')")
    @ApiOperation(value = "文件上传", authorizations = {@Authorization("Authorization")})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "需要进行上传的文件", dataType = "file", paramType = "query", example = "1")
    })
    public Result<String> fileUpload(@RequestParam("file") MultipartFile file) throws IOException {
        // 生成上传的文件名称，UUID+后缀
        String fileName = UUID.randomUUID().toString() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
        // 上传文件 param1: bucketName param2: 文件的名称 param3: 文件的输入流
        ossClient.putObject(bucketName, fileName, file.getInputStream());
        // 返回上传成功, 并提供上传的图片访问的地址路径, 图片的访问地址 https://bucketName.endPoint/fileName
        return Result.success(Constants.HTTPS + bucketName + '.' + endPoint + '/' + fileName);
    }

    /**
     * WEB 直传的方式进行图片的上传，这种方式之前，只是通知一下服务器我要上传图片，
     * 然后服务器返还给客户端一个凭证，用这个凭证直接访问阿里云 OOS 对象上传服务进行上传文件
     * <p>
     * 这里的回调方法没有进行实现，使用的是开发完成的 jar 包服务，在 common 模块中有这个服务，启动即可
     * 启动命令:
     * jar -jar appservermaven-1.0.0.jar 9000
     *
     * @return 统一响应对象
     */
    @GetMapping("/pre/upload")
    // @PreAuthorize("hasAuthority('sys_role_query')")
    @ApiOperation(value = "获取文件上传凭证", authorizations = {@Authorization("Authorization")})
    public Result<Map<String, String>> preUploadPolicy() {
        // 创建上传的文件地址路径
        String path = DateUtil.today().replaceAll("-", "/") + '/';
        // 调用方法获取传输文件的凭据对象
        Map<String, String> policy = getPolicy(30L, 3L * 1024 * 1024, path);
        // 返回用户凭据
        return Result.success(policy);
    }

    /**
     * 获取用户上传文件的凭证或者签名
     * 该功能的书写方法请参考地址：https://help.aliyun.com/zh/oss/use-cases/java-1?spm=a2c4g.11186623.0.0.444a420bsoBHz1
     *
     * @param expireTime  凭据的过期时间
     * @param maxFileSize 最大的文件上传大小
     * @param path        上传文件的路径
     * @return 返回用户传输凭据类 Map 对象
     */
    private Map<String, String> getPolicy(long expireTime, long maxFileSize, String path) {
        long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
        Date expiration = new Date(expireEndTime);
        // PostObject请求最大可支持的文件大小为5 GB，即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
        PolicyConditions policyConds = new PolicyConditions();
        // 设置文件上传的最大最小文件大小
        policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxFileSize);
        // 设置上传的文件上传到那个文件夹
        policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, path);
        // 设置 policy（票据） 的有效时间
        String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
        byte[] binaryData = new byte[0];
        try {
            binaryData = postPolicy.getBytes("utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String encodedPolicy = BinaryUtil.toBase64String(binaryData);
        String postSignature = ossClient.calculatePostSignature(postPolicy);

        Map<String, String> respMap = new HashMap<String, String>();
        respMap.put("accessid", accessKey);
        respMap.put("policy", encodedPolicy);
        respMap.put("signature", postSignature);
        respMap.put("dir", path);
        // 设置上传的主机
        respMap.put("host", Constants.HTTPS + bucketName + '.' + endPoint);
        respMap.put("expire", String.valueOf(expireEndTime / 1000));

        JSONObject jasonCallback = new JSONObject();
        // WEB 直传的方式进行图片的上传之后，回调那个地方，告诉服务器都上传了那些文件，会发送一个 POST 请求。
        jasonCallback.put("callbackUrl", oosCallbackUrl);
        jasonCallback.put("callbackBody",
                "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
        jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded");
        String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
        respMap.put("callback", base64CallbackBody);
        return respMap;
    }
}
